
import sys
import time
import thread
import threading
import traceback

from coffeetalk.protocol import ioelement
from coffeetalk.protocol import access, monitors, responses, iotypes, updates
from coffeetalk.server import extension, ioexceptions, ionode
from coffeetalk.utilities import iologger

class IOTree:

	def __init__(self, logger=None):
	
		if logger != None:
			self.log = logger
		else:
			self.log = iologger.IONullLogger()
				
		self.root = ionode.IONode('/', {})
		self.root.internalnodes['[Access]'] = access.Simple(element=ioelement.IOElement('access', {'type':'IOTP.access.Simple', 'user':'Internal', 'group':'System', 'permissions':'rwsrws---'}))	
		
		self.lock = threading.RLock()
		
	def _find_node(self, env, path, check_access='r', honor_extensions=True):
		"""
		_find_node returns, on successful finding, of the node requested along
		with a list of anchor nodes that need to be notified
		"""
		parentnode = self.root
		node = ''
		anchors = []
		nodes = path.split('/')
		currentpath = '/'
		access = self.root.internalnodes['[Access]']
		
		for node in nodes:
			if node != '':
				currentpath = ''.join((currentpath, node, '/'))			
				if access.check_readaccess(env) == False:
					raise ioexceptions.UnauthorizedError(env, currentpath)
				if parentnode.internalnodes.has_key('[Extension]') and honor_extensions:
					raise ioexceptions.Accepted(env, currentpath, parentnode.internalnodes['[Extension]'].send_request)
				
				if parentnode.internalnodes.has_key('[Anchor]'):
					anchors.insert(0,[parentnode,currentpath])
					
				if parentnode.childelements.has_key(node):
					parentnode = parentnode.childelements[node]
					if parentnode.internalnodes.has_key('[Access]'):
						access = parentnode.internalnodes['[Access]']
				else:
					raise ioexceptions.NotFoundError(env, currentpath)
				
		if check_access == 'r' and access.check_readaccess(env) == False:
			raise ioexceptions.UnauthorizedError(env, path)
		elif check_access == 'w' and access.check_writeaccess(env) == False:
			raise ioexceptions.UnauthorizedError(env, path)
		elif check_access == 's' and access.check_subscribeaccess(env) == False:
			raise ioexceptions.UnauthorizedError(env, path)
		elif parentnode.internalnodes.has_key('[Extension]') and honor_extensions:
			raise ioexceptions.Accepted(env, currentpath, parentnode.internalnodes['[Extension]'].send_request)
		
		return parentnode,anchors
	
	def get_node(self, env, path):
		"""Get an IONode object"""
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='s')
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, 'IOTree.get_node', inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('Get'):
			monitorobject = monitors.Get(env, path)
			self.notify_handlers(node.internalnodes['[Monitors]']['Get'], monitorobject)
		
		returnnode = ionode.IOReturnNode(node)	
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime), bodyelement=returnnode)
		
	def add_node(self, env, parentpath, newnode):
		""" Add an IONode object to the specified parentpath. """
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, parentpath, check_access='w')
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if node.attribs.has_key('type'):
			if node.attribs['type'] == '[Stream]':
				pass
			elif node.attribs['type'] == '[List]' and node.attribs.has_key('require'):
				if newnode.attribs.has_key('type'):
					if newnode.attribs['type'] == node.attribs['require']:
						node.add(newnode)
					else:
						self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'InternalError', 'Invalid Required Type: ' + str(newnode.attribs['type']))
						self.lock.release()
						return responses.InternalError(requestid=env.requestid, path=parentpath, err='Invalid Required Type: ' + str(newnode.attribs['type']))
				else:
					self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'InternalError', 'Invalid Required Type: ' + str(newnode.attribs['type']))
					self.lock.release()
					return responses.InternalError(requestid=env.requestid, path=parentpath, err='Invalid Required Type: ' + str(newnode.attribs['type']))
			else:
				node.add(newnode)
		else:
			node.add(newnode)
		
		if node.internalnodes.has_key('[Subscribers]'):
			updateobject = updates.Add(env, parentpath, newnode)
			self.notify_handlers(node.internalnodes['[Subscribers]'], updateobject)
			
		for anchor in anchors:
			(anchornode,anchorpath) = anchor
			if anchornode.internalnodes.has_key('[AnchorSubscribers]'):
				anchorupdate = updates.AnchorAdd(env,anchorpath,parentpath,newnode)
				self.notify_handlers(anchornode.internalnodes['[AnchorSubscribers]'], anchorupdate)
			
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('Add'):
			monitorobject = monitors.Add(env, parentpath, newnode)
			self.notify_handlers(node.internalnodes['[Monitors]']['Add'], monitorobject)
		
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(parentpath) + str(newnode.name))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=parentpath, time=str(time.time() - starttime))
		
	def delete_node(self, env, parentpath, removekey):
		"""Remove an IONode from the specified parentpath """
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, parentpath, check_access='w')
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
						
		if node.childelements.has_key(removekey):
			node.remove(removekey)
			
			if node.internalnodes.has_key('[Subscribers]'):
				updateobject = updates.Delete(env, parentpath, removekey)
				self.notify_handlers(node.internalnodes['[Subscribers]'], updateobject)
			
			for anchor in anchors:
				(anchornode,anchorpath) = anchor
				if anchornode.internalnodes.has_key('[AnchorSubscribers]'):
					anchorupdate = updates.AnchorDelete(env,anchorpath,parentpath,removekey)
					self.notify_handlers(anchornode.internalnodes['[AnchorSubscribers]'], anchorupdate)
			if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('Delete'):
				monitorobject = monitors.Delete(env, parentpath, removekey)
				self.notify_handlers(node.internalnodes['[Monitors]']['Delete'], monitorobject)
			
		else:
			self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', parentpath + removekey)
			self.lock.release()
			return responses.NotFound(requestid=env.requestid, path=parentpath)
			
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', parentpath + removekey)
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=parentpath, time=str(time.time() - starttime))
			
	def add_subscription(self, env, path, subscriberobject, anchor=False):
		"""Add a subscriber to the path"""
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='s')
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		sub = iotypes.Subscriber(name=str(env.connectionid), username=env.username, group=env.group, handler=subscriberobject)
		if not anchor:
			if not node.internalnodes.has_key('[Subscribers]'):
				node.internalnodes['[Subscribers]'] = {}
			node.internalnodes['[Subscribers]'][str(subscriberobject)] = sub
		else:
			if not node.internalnodes.has_key('[Anchor]'):
				internalerror = ioexceptions.InternalServerError(env,path,'Not an anchor node')
				self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1),'InternalError', path)
				self.lock.release()
				return internalerror.err
				
			if not node.internalnodes.has_key('[AnchorSubscribers]'):
				node.internalnodes['[AnchorSubscribers]'] = {}
			node.internalnodes['[AnchorSubscribers]'][str(subscriberobject)] = sub
			
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('Subscribe'):
			monitorobject = monitors.Subscribe(env, path, sub, anchor=anchor)
			self.notify_handlers(node.internalnodes['[Monitors]']['Subscribe'], monitorobject)
		
		returnnode = ionode.IOReturnNode(node)
		if not anchor:
			self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path) + str('[Subscribers]/') + str(subscriberobject))
		else:
			self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path) + str('[AnchorSubscribers]/') + str(subscriberobject))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime), bodyelement=returnnode)
	
	def delete_subscription(self, env, path, objectid, anchor=False):
		"""Remove a Subscription object from the specified parentpath """
		starttime = time.time()
		self.lock.acquire()
		
		if not isinstance(objectid, str):
			objectid = str(objectid)
			
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path)
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if not anchor:
			if node.internalnodes.has_key('[Subscribers]'):
				if node.internalnodes['[Subscribers]'].has_key(objectid):
					del node.internalnodes['[Subscribers]'][objectid]
					if len(node.internalnodes['[Subscribers]']) == 0:
						del node.internalnodes['[Subscribers]']
					if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('Unsubscribe'):
						monitorobject = monitors.Unsubscribe(env, path, objectid,anchor=anchor)
						self.notify_handlers(node.internalnodes['[Monitors]']['Unsubscribe'], monitorobject)
				else:
					self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', path + '/[Subscribers]/Object:' + objectid)
					self.lock.release()
					return responses.NotFound(requestid=env.requestid, path=path)
			else:
				self.lock.release()
				self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', path + '/[Subscribers]/')
				return responses.NotFound(requestid=env.requestid, path=path)
			
			self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path) + str('[Subscribers]/') + str(objectid))
			self.lock.release()
			return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))
		else:
			if node.internalnodes.has_key('[AnchorSubscribers]'):
				if node.internalnodes['[AnchorSubscribers]'].has_key(objectid):
					del node.internalnodes['[AnchorSubscribers]'][objectid]
					if len(node.internalnodes['[AnchorSubscribers]']) == 0:
						del node.internalnodes['[AnchorSubscribers]']
					if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('Unsubscribe'):
						monitorobject = monitors.Unsubscribe(env, path, objectid, anchor=anchor)
						self.notify_handlers(node.internalnodes['[Monitors]']['Unsubscribe'], monitorobject)
				else:
					self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', path + '/[AnchorSubscribers]/Object:' + objectid)
					self.lock.release()
					return responses.NotFound(requestid=env.requestid, path=path)
			else:
				self.lock.release()
				self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', path + '/[AnchorSubscribers]/')
				return responses.NotFound(requestid=env.requestid, path=path)
			
			self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path) + str('[AnchorSubscribers]/') + str(objectid))
			self.lock.release()
			return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))
		
		
	def add_monitor(self, env, path, monitortype, monitorobject):

		if not hasattr(monitors, monitortype):
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'InternalError', 'Invalid Monitor Type: ' + str(monitortype))
			return responses.InternalError(requestid=env.requestid, path=path, err='Invalid Monitor Type: ' + str(monitortype))

		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path)
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		mon = iotypes.Subscriber(name=str(env.connectionid), username=env.username, group=env.group, handler=monitorobject)
		if not node.internalnodes.has_key('[Monitors]'):
			node.internalnodes['[Monitors]'] = {}
		if not node.internalnodes['[Monitors]'].has_key(monitortype):
			node.internalnodes['[Monitors]'][monitortype] = {}
			
		node.internalnodes['[Monitors]'][monitortype][str(monitorobject)] = mon
		
		bodyelement = None
		if monitortype == 'Subscribe' and node.internalnodes.has_key('[Subscribers]'):
			bodyelement = ioelement.IOElement('Subscribers', {})
			for subscriber in node.internalnodes['[Subscribers]']:
				bodyelement.add(subscriber._to_element())
		elif monitortype == 'Subscribe' and node.internalnodes.has_key('[AnchorSubscribers]'):
			bodyelement = ioelement.IOElement('Subscribers', {})
			for subscriber in node.internalnodes['[AnchorSubscribers]']:
				bodyelement.add(subscriber._to_element())
		elif monitortype == 'AddExtension' and node.internalnodes.has_key('[Extension]'):
			bodyelement = ioelement.IOElement('Extensions', {})
			for extension in node.internalnodes['[Extension]'].clients:
				bodyelement.add(ioelement.IOElement(str(extension), {}))
		elif monitortype == 'AddAccess' and node.internalnodes.has_key('[Access]'):
			bodyelement = node.internalnodes['[Access]']._to_element()
			
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path) + '[Monitors]/' + str(monitortype) + '/' + str(monitorobject))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime), bodyelement=bodyelement)
	
	def delete_monitor(self, env, path, monitortype, objectid):
		
		starttime = time.time()
		self.lock.acquire()
		
		if not isinstance(objectid, str):
			objectid = str(objectid)
			
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path)
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
			
		if node.internalnodes.has_key('[Monitors]'):
			if node.internalnodes['[Monitors]'].has_key(monitortype):
				if node.internalnodes['[Monitors]'][monitortype].has_key(objectid):
					del node.internalnodes['[Monitors]'][monitortype][objectid]
				else:
					self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', path + '/[Monitors]/' + objectid)
					self.lock.release()
					return responses.NotFound(requestid=env.requestid, path=path)

				if len(node.internalnodes['[Monitors]'][monitortype]) == 0:
					del node.internalnodes['[Monitors]'][monitortype]
				if len(node.internalnodes['[Monitors]']) == 0:
					del node.internalnodes['[Monitors]']
			else:
				self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', path + '/[Monitors]/' + objectid)
				self.lock.release()
				return responses.NotFound(requestid=env.requestid, path=path)
		else:
			self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', path + '/[Monitors]/')
			self.lock.release()
			return responses.NotFound(requestid=env.requestid, path=path)
				
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', path + '[Monitors]/'+ str(objectid))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))
	
	def add_access(self, env, path, accessobject):
		
		if env.group != 'System' and env.group != 'Internal':
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'Forbidden', '')
			return responses.Forbidden(requestid=env.requestid, path=path, err='Unable to modify access')
		
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='w')
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if node.internalnodes.has_key('[Access]'):
			accessnode = node.internalnodes['[Access]']
			if isinstance(accessnode, access.Extended):
				accessnode.add_access(accessobject)
			elif isinstance(accessnode, access.Simple):
				newaccess = access.Extended()
				newaccess.from_simple(accessnode)
				newaccess.add_access(accessobject)
				node.internalnodes['[Access]'] = newaccess
		else:
			if isinstance(accessobject, access.Simple) or isinstance(accessobject, access.Extended):
				node.internalnodes['[Access]'] = accessobject
			elif isinstance(accessobject, access.User) or isinstance(accessobject, access.Group) or isinstance(accessobject, access.Others):
				accessnode = access.Extended()
				accessnode.add_access(accessobject)
				node.internalnodes['[Access]'] = accessnode
			else:
				self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'InternalError', 'Invalid Access Type: ' + str(type(accessobject).__name__))
				self.lock.release()
				return responses.InternalError(requestid=env.requestid, path=path, err='Invalid Access Type: ' + str(type(accessobject).__name__))
		
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('AddAccess'):
			monitorobject = monitors.AddAccess(env, path, accessobject)
			self.notify_handlers(node.internalnodes['[Monitors]']['AddAccess'], monitorobject)

		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', path + '[Access]/'+ str(accessobject))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))
	
	def remove_access(self, env, path, accesskey):
		
		if env.group != 'System' and env.group != 'Internal':
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'Forbidden', '')
			return responses.Forbidden(requestid=env.requestid, path=path, err='Unable to modify access')
		
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='w')
		except (ioexceptions.Error), inst:			
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if node.internalnodes.has_key('[Access]'):
			accessnode = node.internalnodes['[Access]']
			if isinstance(accessnode, access.Simple):
				del node.internalnodes['[Access]']
			elif isinstance(accessnode, access.Extended):
				if not accessnode.remove_access(accesskey):
					self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', str(path) + '[Access]/' + str(accesskey))
					self.lock.release()
					return responses.InternalError(requestid=env.requestid, path=path, err='NotFound ' + str(path) + '[Access]/' + str(accesskey))
		else:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', str(path) + '[Access]')
			self.lock.release()
			return responses.InternalError(requestid=env.requestid, path=path, err='NotFound ' + str(path) + '[Access]')
		
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('RemoveAccess'):
			monitorobject = monitors.RemoveAccess(env, path, accesskey)
			self.notify_handlers(node.internalnodes['[Monitors]']['RemoveAccess'], monitorobject)
			
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', path + '[Access]/'+ str(accesskey))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))
	
	def add_extension_client(self, env, path, client):
		
		if env.group != 'System':
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'Forbidden', '')
			return responses.Forbidden(requestid=env.requestid, path=path, err='Unable to add extension')
		
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='w', honor_extensions=False)
		except (ioexceptions.Error), inst:			
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if not node.internalnodes.has_key('[Extension]'):
			node.internalnodes['[Extension]'] = extension.Extension(client, self.log)
		else:
			if isinstance(node.internalnodes['[Extension]'], extension.Extension):
				node.internalnodes['[Extension]'].add_client(client)
		
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('AddExtension'):
			monitorobject = monitors.AddExtension(env, path, ioelement.IOElement(str(client), {}))
			self.notify_handlers(node.internalnodes['[Monitors]']['AddExtension'], monitorobject)
			
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', path + '[Extension]/'+ str(client))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))	
	
	def remove_extension_client(self, env, path, client):
		
		if env.group != 'System':
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'Forbidden', '')
			return responses.Forbidden(requestid=env.requestid, path=path, err='Unable to remove extension')
		
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='w', honor_extensions=False)
		except (ioexceptions.Error), inst:			
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if node.internalnodes.has_key('[Extension]'):
			node.internalnodes['[Extension]'].remove_client(client)
			if node.internalnodes['[Extension]'].client_count() == 0:
				del node.internalnodes['[Extension]']
		else:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', str(path) + '[Extension]/')
			self.lock.release()
			return responses.InternalError(requestid=env.requestid, path=path, err='NotFound ' + str(path) + '[Extension]/')
		
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('RemoveExtension'):
			monitorobject = monitors.RemoveExtension(env, path, ioelement.IOElement(str(client), {}))
			self.notify_handlers(node.internalnodes['[Monitors]']['RemoveExtension'], monitorobject)
			
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', path + '[Extension]/'+ str(client))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))	
	
	def add_anchor(self, env, path):
		"""Mark a node as an anchor node"""
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='s')
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if node.internalnodes.has_key('[Anchor]'):
			self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'InternalError', 'Anchor already set')
			self.lock.release()
			return responses.InternalError(requestid=env.requestid, path=path, err='Anchor already set')
		else:
			node.internalnodes['[Anchor]'] = True
		
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('AddAnchor'):
			monitorobject = monitors.AddAnchor(env,path)
			self.notify_handlers(node.internalnodes['[Monitors]']['AddAnchor'], monitorobject)
			
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))
		
	def remove_anchor(self, env, path):
		"""Mark a node as an anchor node"""
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='s')
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if not node.internalnodes.has_key('[Anchor]'):
			self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', str(path))
			self.lock.release()
			return responses.InternalError(requestid=env.requestid, path=path, err='NotFound ' + str(path) + '[Anchor]')
		else:
			node.internalnodes['[Anchor]'] = False
		
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('RemoveAnchor'):
			monitorobject = monitors.AddAnchor(env,path)
			self.notify_handlers(node.internalnodes['[Monitors]']['RemoveAnchor'], monitorobject)
			
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime))
	
	def get_internal_node(self, env, path, internaltype):
		"""Get an IONode object"""
		starttime = time.time()
		self.lock.acquire()
		
		node = None
		anchors = []
		try:
			(node, anchors) = self._find_node(env, path, check_access='s')
		except (ioexceptions.Error), inst:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), inst.name, inst.err.path)
			self.lock.release()
			return inst.err
		
		if node.internalnodes.has_key(internaltype):
			pass
		else:
			self.log.log_error(iologger.WARNING, env.ip, env.username, env.group, iologger.current_function(depth=1), 'NotFound', str(path) + internaltype + '/')
			self.lock.release()
			return responses.InternalError(requestid=env.requestid, path=path, err='NotFound ' + str(path) + internaltype + '/')
		
		if node.internalnodes.has_key('[Monitors]') and node.internalnodes['[Monitors]'].has_key('Get'):
			monitorobject = monitors.Get(env, path)
			self.notify_handlers(node.internalnodes['[Monitors]']['Get'], monitorobject)
		
		returnnode = ionode.IOReturnNode(node)	
		self.log.log_access(env.ip, env.username, env.group, iologger.current_function(depth=1), 'OK', str(path))
		self.lock.release()
		return responses.OK(requestid=env.requestid, path=path, time=str(time.time() - starttime), bodyelement=returnnode)
	
	def notify_handlers(self, node, notifyobject):
		
		notifystring = str(notifyobject)
		
		for notifyhandler in node.itervalues():
			try:
				notifyhandler.handler(notifystring)
			except:
				traceback.print_exc(file=sys.stderr)
				type, value = sys.exc_info()[:2]
				self.log.log_error(iologger.CRITICAL, '0.0.0.0', 'Internal', 'Internal', iologger.current_function(depth=0), 'InternalError', str(type) + ':' + str(value))
				
class NullIOTree(IOTree):
	
	def __init__(self, logger = None):
		if logger != None:
			self.log = logger
		else:
			self.log = iologger.IONullLogger()
		self.lock = thread.allocate_lock()
		self.root = ionode.IONode('/', {})
	
	def _find_node(self, env, path, check_access='r', honor_extensions=True):
		raise ioexceptions.NotFoundError(env, path)
		
